package com.me.myFramework.aop22.aop111;

import com.me.myFramework.aop22.aop111.advice.method.AfterReturningAdvice111;
import com.me.myFramework.aop22.aop111.advice.method.MethodBeforeAdvice111;
import com.me.myFramework.aop22.aop111.intercept.method.AfterReturningAdviceInterceptor111;
import com.me.myFramework.aop22.aop111.intercept.method.MethodBeforeAdviceInterceptor111;
import com.me.myFramework.aop22.IBuy;
import com.me.myFramework.aop22.Switch;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 《自己实现 AOP-111》  <br><br>
 *
 * TODO 以下变更点：<ol>
 *     <li>
 *         把模板类中的抽象方法提取成 Advice（增强逻辑）接口，把 模板方法 和 advice 分离，以后实现切面只要关注 advice，不需要关注模板类：<br>
 *           MethodBeforeAdvice：前置增强逻辑.<br>
 *           AfterReturningAdvice：后置增强逻辑
 *     </li>
 *  </ol>
 *
 * @author ME
 */
public class MyAop111 {

    /**
     * 通过 模板模式 提取增强逻辑：方法执行前、正常返回、异常返回
     */
    public static void main(String[] args) throws Throwable {
        IBuy target = new Switch();

        IBuy proxy = (IBuy) new MyAop111().getProxy1(target, new MethodBeforeAdvice111() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.println("[AOP] 就想看看谁买东西了：" + args[0]);
            }
        });
        proxy.buy("Jack");

        System.out.println("\n----------------------------------------------");
        proxy = (IBuy) new MyAop111().getProxy2(target, new AfterReturningAdvice111() {
            @Override
            public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
                System.out.println("[AOP2] 记录购买价格 " + returnValue);
            }
        });
        proxy.buy("Tom");
    }

    private Object getProxy1(Object target, MethodBeforeAdvice111 advice) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (isCustomMethod(method.getName())) {
                    MethodBeforeAdviceInterceptor111 interceptor = new MethodBeforeAdviceInterceptor111(advice);
                    return interceptor.invoke(method, args, target);
                }

                return method.invoke(target, args);
            }
        });
    }

    private Object getProxy2(Object target, AfterReturningAdvice111 advice) {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (isCustomMethod(method.getName())) {
                    AfterReturningAdviceInterceptor111 interceptor = new AfterReturningAdviceInterceptor111(advice);
                    return interceptor.invoke(method, args, target);
                }

                return method.invoke(target, args);
            }
        });
    }

    boolean isCustomMethod(String methodName) {
        return !("toString".equals(methodName) || "hashCode".equals(methodName) || "equals".equals(methodName)
                || "clone".equals(methodName));
    }

}
